import { Watcher } from "./watcher.js"

export const Compiler = class {
  constructor(vm, el) {
    this.$el = document.querySelector(el)
    // 这个是 kvue 的实例对象
    this.$vm = vm
    this.compiler(this.$el)
  }
  compiler(node) {
    let nodeChilds = node.childNodes
    Array.from(nodeChilds).forEach(nodeChild => {
      // 如果是元素
      if (this.isElement(nodeChild)) {
        Array.from(nodeChild.attributes).forEach(attribute => {
          let attrName = attribute.name
          let attrVal = attribute.value
          if (this.isDirective(attrName)) {
            let exp = attrName.substring(2)
            // 执行我的更新函数和收集操作
            this[exp] && this[exp](nodeChild, attrVal)
          }
          if (this.isEvent(attrName)) {
            let exp = attrName.substring(1)
            this.eventHander(node, exp, attrVal)
          }
        })
      }
      // 如果是文本内容
      if (this.inNer(nodeChild)) {
        this.compileText(nodeChild)
      }

      // 如果后续还存在内容 进行递归处理
      if (nodeChild.childNodes && nodeChild.childNodes.length) {
        this.compiler(nodeChild)
      }
    })
  }
  // 统一的更新操作
  update(node, key, dir) {
    // 执行更新
    let runFn = this[dir + 'Compiler']
    runFn && runFn(node, this.$vm[key])
    // 执行收集
    new Watcher(this.$vm, key, function (val) {
      // 注意我在里面将这个文件的绑定搞到 kvue上面去了
      runFn && runFn(node, val)
    })
  }

  html(node, val) {
    // node.innerHTML = this.$vm[val]
    this.update(node, val, 'html')
  }
  text(node, val) {
    // node.textContent = this.$vm[val]
    this.update(node, val, 'text')
  }
  model(node, val) {
    // 执行我的更新和依赖收集
    this.update(node, val, 'model')
    // 更新我的事件
    node.addEventListener('input', (e) => {
      this.$vm[val] = e.target.value
    })
  }
  isEvent(attrName) {
    return attrName.indexOf('@') == 0
  }

  isDirective(attrName) {
    return attrName.indexOf('k-') == 0
  }
  isElement(node) {
    return node.nodeType == 1
  }
  inNer(node) {
    return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent)
  }
  compileText(node) {
    // node.textContent = this.$vm[RegExp.$1]
    this.update(node, RegExp.$1, 'text')
  }

  htmlCompiler(node, val) {
    node.innerHTML = val
  }
  textCompiler(node, val) {
    node.textContent = val
  }
  modelCompiler(node, val) {
    node.value = val
  }
  eventHander(node, dir, method) {
    let fn = this.$vm.$options.methods && this.$vm.$options.methods[method]
    node.addEventListener(dir, fn.bind(this.$vm))
  }
}
